package com.sky.dish.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.sky.category.mapper.CategoryMapper;
import com.sky.constant.MessageConstant;
import com.sky.constant.StatusConstant;
import com.sky.dish.mapper.DishMapper;
import com.sky.dish.service.DishService;
import com.sky.dishflavor.service.DishFlavorService;
import com.sky.dto.DishDTO;
import com.sky.dto.DishPageQueryDTO;
import com.sky.entity.Category;
import com.sky.entity.Dish;
import com.sky.entity.DishFlavor;
import com.sky.entity.Setmeal;
import com.sky.exception.DeletionNotAllowedException;
import com.sky.exception.DishNotFoundException;
import com.sky.result.PageResult;
import com.sky.setmealdish.service.SetMealDishService;
import com.sky.vo.DishVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

/**
 * 菜品管理 serviceImpl
 */
@Slf4j
@Service
public class DishServiceImpl implements DishService {

    @Resource
    private DishMapper dishMapper;

    @Resource
    private DishFlavorService dishFlavorService;

    @Resource
    private SetMealDishService setMealDishService;

    @Resource
    private CategoryMapper categoryMapper;



    /**
     * 菜品管理
     *        新增菜品
     * @param dishDTO
     */
    @Override
    @Transactional(rollbackFor=Exception.class, propagation= Propagation.REQUIRED) //开启事务
    public void saveDishAndFlavor(DishDTO dishDTO) {
        AtomicInteger flavorCount = new AtomicInteger(0);

        // 1.菜品表存入1条数据
        // 1.1类型拷贝  Aop补充字段
        Dish dish = JSON.parseObject(JSON.toJSONString(dishDTO, SerializerFeature.WriteMapNullValue), Dish.class);
        dish.setStatus(StatusConstant.ENABLE);
        Integer dishCount = dishMapper.insert(dish);

        // 2.口味表存入n条数据  先判断是否有值
        if( !CollectionUtils.isEmpty(dishDTO.getFlavors()) ){
            // 2.1口味集合 过滤空数据
            List<DishFlavor> flavorList = dishDTO.getFlavors().stream().filter(Objects::nonNull).collect(Collectors.toList());
            // 2.2绑定菜品id
            flavorList.forEach(f->f.setDishId(dish.getId()));
            // 2.3遍历，插入数据库
            for (DishFlavor dishFlavor : flavorList) {
                dishFlavorService.insert(dishFlavor);
                flavorCount.incrementAndGet();
            }
        }
        log.info("新增菜品,影响行数: {}, 新增口味,影响行数: {}", dishCount, flavorCount.get());
    }



    /**
     * 菜品管理
     *        分页条件查询
     * @param dishPageQueryDTO
     */
    @Override
    public PageResult pageQuery(@Validated DishPageQueryDTO dishPageQueryDTO) {
        // 1.PageHelper设置分页参数  排序参数
        PageHelper.startPage(dishPageQueryDTO.getPage(), dishPageQueryDTO.getPageSize());
        // 2.查询返回的集合  封装为PageHelper的集合
        Page<DishVO> page = dishMapper.selectPage(dishPageQueryDTO);
        // 3.返回
        return new PageResult(page.getTotal(), page.getResult());
    }



    /**
     * 菜品管理
     *         根据ids删除菜品 及 口味
     *         单条删除      多条删除
     * @param dishIds
     */
    @Override
    @Transactional(rollbackFor= Exception.class, propagation= Propagation.REQUIRED) //开启事务
    public void deleteByIds(List<Long> dishIds) {
        // 1.判断是否能够删除菜品 --- 是否是起售状态的菜品
        List<Dish> dishList = dishMapper.selectStatusByIds(dishIds);
        if( !CollectionUtils.isEmpty(dishList) ){
            String nameStr = dishList.stream().filter(Objects::nonNull)
                                              .filter(dish -> StringUtils.hasLength(dish.getName()))
                                              .map(Dish::getName)
                                              .collect(Collectors.joining(";"));
            //有起售状态的菜品，抛出异常
            throw new DeletionNotAllowedException(MessageConstant.DISH_ON_SALE + nameStr);
        }

        // 2.判断是否能够删除菜品 --- 是否关联了套餐
        List<Setmeal> setmealList = setMealDishService.selectSetMealListByDishIds(dishIds);
        if( !CollectionUtils.isEmpty(setmealList) ){
            String nameStr = setmealList.stream().filter(Objects::nonNull)
                                                 .filter(setmeal -> StringUtils.hasLength(setmeal.getName()))
                                                 .map(Setmeal::getName)
                                                 .collect(Collectors.joining("'"));
            //有关联套餐，抛出异常
            throw new DeletionNotAllowedException(MessageConstant.DISH_BE_RELATED_BY_SETMEAL + nameStr);
        }
        // 3.停售状态 且 没有关联套餐  可以删除
        // 同时删除菜品以及口味数据
        Integer dishCount = dishMapper.deleteInIds(dishIds);
        Integer flavorCount = dishFlavorService.deleteByDishIds(dishIds);
        log.info("当前菜品ids有: {}条,共删除菜品: {}条, 所有菜品所关联的口味共删除: {}条", dishIds.size(), dishCount, flavorCount);
    }


    /**
     * 菜品管理
     *        根据id查询菜品 以及 口味
     * @param id
     */
    @Override
    public DishVO selectDishAndFlavorByDishId(Long id) {
        // 1.查询菜品 并判断
        Dish dish = dishMapper.selectById(id);
        if(Objects.isNull(dish)){
            throw new DishNotFoundException(MessageConstant.DISH_IS_NULL);
        }
        // 2.对象拷贝
        DishVO dishVO = JSON.parseObject(JSON.toJSONString(dish, SerializerFeature.WriteMapNullValue), DishVO.class);
        // 3.查询菜品所属分类
        Category category = categoryMapper.selectById(dish.getCategoryId());
        if(Objects.nonNull(category) && StringUtils.hasLength(category.getName())){
            dishVO.setCategoryName(category.getName());
        }
        // 4.查询菜品关联的口味集合
        List<DishFlavor> flavorList = dishFlavorService.getByDishId(id);
        if( !CollectionUtils.isEmpty(flavorList) ){
            dishVO.setFlavors(flavorList);
        }
        return dishVO;
    }



    /**
     * 菜品管理
     *        修改菜品及口味
     * @param dishDTO
     */
    @Override
    @Transactional(rollbackFor= Exception.class, propagation= Propagation.REQUIRED) //开启事务
    public void updateDishAndFlavor(DishDTO dishDTO) {
        AtomicInteger flavorCount = new AtomicInteger(0);
        // 1.修改菜品数据
        // 1.1对象拷贝
        Dish dish = JSON.parseObject(JSON.toJSONString(dishDTO, SerializerFeature.WriteMapNullValue), Dish.class);
        Integer dishCount = dishMapper.updateById(dish);
        // 2.删除关联的口味数据
        Integer deleteFlavorCount = dishFlavorService.deleteByDishIds(Collections.singletonList(dishDTO.getId()));
        // 3.添加新的菜品口味数据
        if( !CollectionUtils.isEmpty(dishDTO.getFlavors()) ){
            List<DishFlavor> flavorList = dishDTO.getFlavors();
            //给菜品口味数据设置菜品id
            flavorList.forEach(flavor -> flavor.setDishId(dishDTO.getId()));
            for (DishFlavor dishFlavor : flavorList) {
                dishFlavorService.insert(dishFlavor);
                flavorCount.incrementAndGet();
            }
        }
        log.info("修改菜品id: {},影响行数: {},删除原始菜品口味: {}条数据,新增: {}条口味数据",dishDTO.getId(), dishCount, deleteFlavorCount, flavorCount.get());
    }



    /**
     * 菜品管理
     *        起售/停售
     * @param id
     * @param status
     */
    @Override
    public void updateStatus(Long id, Integer status) {
        // 1.封装数据 aop需要实体类
        Dish dish = Dish.builder().id(id).status(status).build();
        // 2.调方法
        Integer count = dishMapper.updateById(dish);
        log.info("菜品id: {},起售/停售影响行数: {}", id, count);
    }



    /**
     * 菜品管理
     *        根据分类id查询菜品列表
     * @param categoryId
     */
    @Override
    public List<Dish> selectDishListByCategoryId(Long categoryId) {
        return dishMapper.selectDishListByCategoryId(categoryId);
    }



    /**
     * 菜品管理
     *       控制台展示统计的 禁售/起售 数量
     */
    @Override
    public Integer countDishStatusByMap(Map map) {
        return dishMapper.countDishStatusByMap(map);
    }
}
